-
Notifications
You must be signed in to change notification settings - Fork 569
feat: New parameter and cli arguments handling #1060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ros2
Are you sure you want to change the base?
Conversation
Don't retrieve ROS parameters in rosbridge_library, instead, use parameters dictionary passed to RosbridgeProtocol for everything
Instead of passing them using a class variable
Respect max_message_size parameter when determining fragment size, even when client provides fragment_size parameter
I won't get to review this for a bit, but have you considered possibly using https://github.com/PickNikRobotics/generate_parameter_library? Not sure how well this will do with hybrid ROS params and CLI arguments though, but at least may package up your params in a neat struct. |
Might be a good idea. I'll try to figure out whether I can use it together with cli args |
@sea-bass I looked at the possibility of using generate_parameter_library and IMO it's not worth it. It will only add complexity instead of simplifying it and won't provide any useful features. All of the parameters are read-only (read only once at the start) and don't require any validation. Also it's not possible to retrieve parameter descriptions for cli args before declaring ROS parameters. |
@sea-bass @EzraBrooks @MatthijsBurgh I think it's ready now. Would appreciate a review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look cleaner! Have a few comments throughout.
Also for what it's worth, generate_parameter_library
is not just read only once at the beginning. It can definitely make parameters runtime tunable. But it does rely on more tools and code generation that I am convinced are not necessary here.
self.protocol = protocol | ||
|
||
if self.parameter_names and self.protocol.parameters: | ||
for param_name in self.parameter_names: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and the next line could be a single list comprehension
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you show what specific you have in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for param_name in self.parameter_names if param_name in self.protocol.parameters:
setattr(self, param_name, self.protocol.parameters[param_name])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... actually I just noticed another option
for param_name, param_value in self.parameter_names.items():
if param_name in self.protocol.parameters:
setattr(self, param_name, param_value)
this isn't a major comment, i was just staring at python
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually a thing? I haven't seen such syntax in python
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested:
for name in list1 if name in list2:
print(name)
And it gave me syntax error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ugh no, I missed a thing. It would actually be this, which already makes it too hard to read
for param_name in [p for p in self.parameter_names if p in self.protocol.parameters]:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, this starts to look ugly. I might go with your other proposition though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think #1060 (comment) may still be worthwhile though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... actually I just noticed another option
for param_name, param_value in self.parameter_names.items(): if param_name in self.protocol.parameters: setattr(self, param_name, param_value)this isn't a major comment, i was just staring at python
I cannot use items()
on parameter_names
as it is a tuple of strings, not a dictionary. This would have to iterate over parameters
instead:
if self.parameter_names and self.protocol.parameters:
for param_name, param_value in self.protocol.parameters.items():
if param_name in self.parameter_names:
setattr(self, param_name, param_value)
|
||
if parameters is not None: | ||
if "binary_encoder" in parameters: | ||
binary_encoder_type = parameters["binary_encoder"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than globals (yuck, though they were here before), I would do something like
binary_encoder_type = parameters.get("binary_encoder", None)
(Of course the , None
is not needed above, but putting there for other parameters that have different defaults
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I see that those globals are used by the other free functions, so are they necessary? Hmmn, what if configure()
simply returns the values it needs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
binary_encoder_type
global is not used by any function so it can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed binary_encoder_type
, one less global. binary_encoder
and bson_only_mode
are used by the functions though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had an idea for a refactor which would add a MessageConverter
class that would have the extract_values
and populate_instance
methods and would take the protocol parameters in the constructor. The Protocol
class would create an instance of the converter and use it instead of calling the free functions. This would allow to configure the converter with different parameters per Protocol instance (allowing for example using BSON and JSON at the same time).
When I started refactoring it, it turned out it would require changes in many places and I decided to not do this to not overcomplicate this PR.
Let's be honest here. The way parameters are passed down from rosbridge_server to the Rosbridge protocol and internal classes is a complete mess:
Protocol.parameters
dictionary.publishers.py
ormessage_conversion.py
implement a singleton interface that expect parameters to be passed to them but they're either not passed or passed in a non-standard way.This PR contains the following modifications:
Protocol
class now allows passing the parameters through the constructor instead of class variableprotocol.parameters
upon initialization (No need to settopics_glob
class variables) and use instance variables to get them later (e.g.self.topics_glob
instead ofAdvertise.topics_glob
).protocol.parameters
dictionary contains the keys.argparse
for parsing cli arguments (less boilerplate code and more user-friendly interface)protocol_parameters
is set inRosbridgeWebSocket
handler, no need to create it on every opened connectionactions_glob
parameterpublishers.py
andmessage_conversion.py
modules are now configured upon first initialization of the Protocol class.Some changes in behavior:
message_conversion.py
module needs to be configured before use. This is reflected in thetest_message_conversion.py
test.binary_encoder_type
andbson_only_mode
are now actually passed tomessage_conversion
module and have effect.max_message_size
actually has effect now, that is, even when client sets thefragment_size
, the actual fragment size is capped tomax_message_size
value.What's left to do:I need to figure out how the bson parameters are handled, especially the relation betweenbinary_encoder
andbson_only_mode
.Some real life application testingEven though this is still WIP, I would appreciate a review